/*
 * Broadcast.cpp
 *
 *  Created on: 08/ott/2013
 *      Author: alessandrob
 */
#include<iostream>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<opencv2/opencv.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/photo/photo.hpp>
#include "Utility.h";
#include "brisk/brisk.h"
#define BUFLEN 1400
#define PORT 5002
#define PORTB 5003
#define RECEIVE 3
#define ROWS 640
#define OVERLAP 15
using namespace std;
using namespace cv;

void broadcast(int threshold,int octave)
{
	cout<<"Setting up BRISK..."<<endl;
	struct sockaddr_in server_address,broadcast_address,my_address;
	char ready[5];
	char ack;
	char sync[30];
	int sockfd,dataReceived,cols,rows;
	int blocks,receivedBlocks=0,CPUspeed,clientsNumber,myIpAddress;
	Mat picture;
	bool finished = false;
	double det_time,processing_time,sumProc = 0;
	unsigned int slen=sizeof(server_address);
	//unsigned int slenb=sizeof(broadcastPermission);
	Mat descriptors;
	vector<KeyPoint> kp;
	vector<int> params = vector<int>(2);
	params[0]=0;

	Mat test = imread("/home/ubuntu/test.jpg",0);
	//system("cpufreq-set -f 720MHz");
	Ptr<FeatureDetector> detector = new cv::BriskFeatureDetector(threshold,octave);
	Ptr<DescriptorExtractor> descriptorExtractor = new cv::BriskDescriptorExtractor();
	//first time
	detector->detect(test,kp);
	descriptorExtractor->compute(test,kp,descriptors);
	//system("cpufreq-set -f 275MHz");
	/*Codice per la stima della velocità del processore in bit/s.
   for(int i=0;i<10;i++)
	{
		det_time=(double)getTickCount();
		detector->detect(test,kp);
		descriptorExtractor->compute(test,kp,descriptors);
		processing_time = ((double)getTickCount() - det_time)/getTickFrequency();
		sumProc+=processing_time;
	}
	CPUspeed = (int)(test.cols*test.rows*8)/(sumProc/10);
	cout<<"CPU speed: "<<CPUspeed<<endl;
	cout<<"Testing keypoints found: "<<kp.size()<<endl;*/
   
	CPUspeed = 1657181;
	//UDP Socket
	cout<<"Setting up UDP client..."<<endl;
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0))==-1)
		printErrorLog("Client socket not created correctly.");
	bzero(&my_address, sizeof(my_address));
	my_address.sin_family = AF_INET;
	//Listening on 5002
	my_address.sin_port = htons(PORT);
	my_address.sin_addr.s_addr = htonl(INADDR_BROADCAST);
	if (bind(sockfd, (struct sockaddr *)&my_address, sizeof(my_address))==-1)
		printErrorLog("Wrong binding. The client is NOT running");
	char* myIp = inet_ntoa(my_address.sin_addr);
	myIpAddress = atoi(&myIp[8]);
	bzero(&server_address, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(PORT);
	char ip[16];
	memmove(ip,"10.42.0.5",sizeof(ip));
	if(inet_aton(ip,&server_address.sin_addr)==0)
		printErrorLog("IP address not correct");
	cout<<"Ready for the connection..."<<endl;
	ready[0]='r';
	ready[1]= (CPUspeed >> 24);
	ready[2]= (CPUspeed >> 16);
	ready[3]= (CPUspeed >> 8);
	ready[4]= (CPUspeed);
  //Invio del pacchetto di connessione, contenente la velocità del processore del nodo (stimata o settata
  //in precedenza. 
	if (sendto(sockfd, ready, sizeof(char)*5, 0, (struct sockaddr*)&server_address, slen)==-1)
		printErrorLog("Connection request failed");
	cout<<"Connection done. Ready to get synchronized..."<<endl;
	dataReceived = recvfrom(sockfd,&sync[0],sizeof(char)*30,0,(struct sockaddr*)&broadcast_address,&slen);
	cout<<"Sync ACK length: "<<dataReceived<<endl;
	clientsNumber = (sync[0]<<24);
	clientsNumber = clientsNumber+(sync[1]<<16);
	clientsNumber = clientsNumber+(sync[2]<<8);
	clientsNumber = clientsNumber+(sync[3]);
	//printf("Byte read: %d\n",sync[0]);
	if(dataReceived!=sizeof(char)*30)
		printErrorLog("Synchronization failed");
	if(clientsNumber > 0 && clientsNumber < 5)
	{
		cout<<"Got a sync ACK from: "<<inet_ntoa(broadcast_address.sin_addr)<<endl;
		//Synchronization
		cout<<"Synchronization done. Timer started"<<endl;
		det_time=(double)getTickCount();
		//RECEIVING PACKETS
		//Ricezione del pacchetto contenente le informazioni di tutti i client.
		uchar infoPacket[12+clientsNumber*8];
		if(recvfrom(sockfd,&infoPacket[0],sizeof(char)*12 + clientsNumber*8,0,(struct sockaddr*)&server_address,&slen)==-1)
			printErrorLog("Information packet not received");
		cout<<"Information packet received"<<endl;
		cols = (infoPacket[0]<<24);
		cols = cols+(infoPacket[1]<<16);
		cols = cols+(infoPacket[2]<<8);
		cols = cols+(infoPacket[3]);
		rows = (infoPacket[4]<<24);
		rows = rows+(infoPacket[5]<<16);
		rows = rows+(infoPacket[6]<<8);
		rows = rows+(infoPacket[7]);
		blocks = (infoPacket[8]<<24);
		blocks = blocks+(infoPacket[9]<<16);
		blocks = blocks+(infoPacket[10]<<8);
		blocks = blocks+(infoPacket[11]);
		int counter = 12;
		int address[clientsNumber],load[clientsNumber],slice = 0;
		for(int i=0;i<clientsNumber;i++,counter=counter+8)
		{
			address[i] = (infoPacket[counter] << 24);
			address[i] = address[i] + (infoPacket[counter+1] << 16);
			address[i] = address[i] + (infoPacket[counter+2] << 8);
			address[i] = address[i] + (infoPacket[counter+3]);
			load[i] = (infoPacket[counter+4] << 24);
			load[i] = load[i] + (infoPacket[counter+5] << 16);
			load[i] = load[i] + (infoPacket[counter+6] << 8);
			load[i] = load[i] + (infoPacket[counter+7]);
			if(address[i]==myIpAddress)
				slice=i;
		}
		cout<<"Columns: "<<cols<<endl;
		cout<<"Rows: "<<rows<<endl;
		cout<<"Blocks: "<<blocks<<endl;
		int bytesPerPacket = (int)(cols*rows)/blocks;
		int lastPacket = (cols*rows)-(bytesPerPacket*blocks);
		cout<<"Bytes per packet: "<<bytesPerPacket<<endl;
		cout<<"Last packet: "<<lastPacket<<endl;
		broadcast_packet packet[blocks];
		uchar buffer[bytesPerPacket+8];
		//Receiving data
		double start_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Start receiving packets time: T+"<<start_time*1000<<"ms"<<endl;

		//RECEIVING THE BROADCAST IMAGE
		while(!finished)
		{
			cout<<"packet: "<<receivedBlocks+1<<endl;
			dataReceived=recvfrom(sockfd,&buffer[0],bytesPerPacket+8,0,(struct sockaddr*)&broadcast_address,&slen);
			if(dataReceived == 3 && buffer[0]=='e' && buffer[1]=='n' && buffer[2]=='d')
				finished = true;
				//receivedBlocks++;
			else if(dataReceived==(lastPacket+8))
			{
				for(int k=0;k<(lastPacket+8);k++)
					packet[receivedBlocks].serializedPacket[k]=buffer[k];
				receivedBlocks++;
			}
			else if(dataReceived==(bytesPerPacket+8))
			{
				for(int k=0;k<(bytesPerPacket+8);k++)
					packet[receivedBlocks].serializedPacket[k]=buffer[k];
				receivedBlocks++;
			}
		}
		finished = false;
		cout<<"Blocks received: "<<receivedBlocks<<"/"<<blocks<<endl;

		double rec_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Receiving time: T+"<<rec_time*1000<<"ms"<<endl;
		//Deserializing packets
		deserializePackets(packet,receivedBlocks,blocks);
		double d_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Deserializing packet time: T+"<<d_time*1000<<"ms"<<endl;
		//Rebuilding the image
		if(lastPacket>0)
			blocks = blocks + 1;
		//Building image
		Mat picture;
		uchar data[cols*rows];
		int packetNumber = 1,offset=0;
		broadcast_packet imageReceivedData[blocks];

		//BUILDING IMAGE WITH LOST PACKETS
		for(int i=0;packetNumber<(blocks+1);packetNumber++)
		{
			if(packet[i].packetNumber == packetNumber)
			{
				imageReceivedData[packetNumber-1].lost = false;
				imageReceivedData[packetNumber-1].packetNumber = packetNumber;
				if(packetNumber == blocks && lastPacket>0)
				{
					imageReceivedData[packetNumber-1].payloadDimension = lastPacket;
					//normal last packet
					for(int k=0;k<lastPacket;k++)
					{
						data[offset+k]=packet[i].serializedPacket[k+8];
						imageReceivedData[packetNumber-1].serializedPacket[k] = packet[i].serializedPacket[k+8];
					}
					offset = offset + lastPacket;
					i++;
				}
				else
				{
					imageReceivedData[packetNumber-1].payloadDimension = bytesPerPacket;
					//normal packet
					for(int k=0;k<bytesPerPacket;k++)
					{
						data[offset+k]=packet[i].serializedPacket[k+8];
						imageReceivedData[packetNumber-1].serializedPacket[k] = packet[i].serializedPacket[k+8];
					}
					offset = offset + bytesPerPacket;
					i++;
				}
			}
			else
			{
				imageReceivedData[packetNumber-1].lost = true;
				//missing packet
				for(int k=0;k<bytesPerPacket;k++)
					data[offset+k]=255;
				offset = offset + bytesPerPacket;
			}
		}
		picture = Mat(rows,cols,CV_8UC1,data,cols);
		imwrite("/home/ubuntu/NotRecoveredImage.jpg",picture,params);
		double b_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Building picture with lost packets time: T+"<<b_time*1000<<"ms"<<endl;

		//SENDING LOST PACKETS
		//setting TCP socket in order to have a reliable communication
		if ((sockfd = socket(AF_INET, SOCK_STREAM, 0))==-1)
			printErrorLog("Client socket not created correctly.");
		bzero(&server_address, sizeof(server_address));
		server_address.sin_family = AF_INET;
		server_address.sin_port = htons(PORT);
		char ip[16];
		memmove(ip,"10.42.0.5",sizeof(ip));
		if(inet_aton(ip,&server_address.sin_addr)==0)
			printErrorLog("IP address not correct");
    //Connessione TCP con il server.     
		if(connect(sockfd,(const struct sockaddr*)&server_address,sizeof(server_address))<0)
			printErrorLog("Connection refused");
		cout<<"Recovering the image"<<endl;
		int lostPackets = blocks - receivedBlocks;

		//RECOVERING BROADCAST IMAGE
		//Ricezione ack per l'inizio dell'invio dei pacchetti persi.
		if(recv(sockfd,&ack,sizeof(char),0)<0)
			printErrorLog("Ack reception failed");
		//Pacchetto contenente il numero di pacchetti effettivamente persi.
		if(send(sockfd, &lostPackets , sizeof(int), 0)!=sizeof(int))
			printErrorLog("Sending lost packets failed");
		char InfoLostPackets[lostPackets*4];
		int count = 0;
		//Pacchetto contenente tutti i numeri identificativi di ogni pacchetto perso.
		for(int i = 0;i<lostPackets;i++)
		{
			for(int k = 0;k < blocks;k++)
			{
				if(imageReceivedData[k].lost == true)
				{
					InfoLostPackets[count] = (imageReceivedData[k].packetNumber >> 24);
					InfoLostPackets[count+1] = (imageReceivedData[k].packetNumber >> 16);
					InfoLostPackets[count+2] = (imageReceivedData[k].packetNumber >> 8);
					InfoLostPackets[count+3] = (imageReceivedData[k].packetNumber);
					cout<<"Packet lost: "<<imageReceivedData[k].packetNumber<<endl;
					count = count + 4;
				}
			}
		}
		//Invio dei pacchetti
		if(send(sockfd, &InfoLostPackets[0] , sizeof(char)*4*lostPackets, 0)<0)
			printErrorLog("Sending lost packets failed");
		int payloadDimension;
		//Ricezione dei pacchetti e recovering dell'immagine
		for(int i = 0;i < lostPackets;i++)
		{
			dataReceived = recv(sockfd,&buffer[0],bytesPerPacket+8,0);
			if(dataReceived < 0)
				printErrorLog("Lost packets reception failed");
			payloadDimension = (buffer[0] << 24);
			payloadDimension = payloadDimension + (buffer[1] << 16);
			payloadDimension = payloadDimension + (buffer[2] << 8);
			payloadDimension = payloadDimension + (buffer[3]);
			packetNumber = (buffer[4] << 24);
			packetNumber = packetNumber + (buffer[5] << 16);
			packetNumber = packetNumber + (buffer[6] << 8);
			packetNumber = packetNumber + (buffer[7]);
			cout<<"Packet: "<<packetNumber<<" received"<<endl;
			for(int k = 0; k < payloadDimension;k++)
				//only the raw data of the image
				imageReceivedData[packetNumber-1].serializedPacket[k] = buffer[k+8];
			imageReceivedData[packetNumber-1].packetNumber = packetNumber;
			imageReceivedData[packetNumber-1].payloadDimension = payloadDimension;
			imageReceivedData[packetNumber-1].lost = false;
		}
		//Array uchar dell'immagine completa 
		offset = 0;
		for(int i=0;i<blocks;i++)
		{
			for(int k=0;k<imageReceivedData[i].payloadDimension;k++)
				data[offset+k]=imageReceivedData[i].serializedPacket[k];
			offset = offset + imageReceivedData[i].payloadDimension;
		}
		picture = Mat(rows,cols,CV_8UC1,data,cols);
		imwrite("/home/ubuntu/recoveredImage.jpg",picture,params);
		double recovering_image_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Recovering image time: T+"<<recovering_image_time*1000<<"ms"<<endl;

		//SPlitting dell'immagine broadcast basandosi sul pacchetto informazione
    //ricevuto in precedenza.  
		int overlap = (int)(ROWS*OVERLAP)/100;
		int r,y = 0;
		Mat splitBroadcastImage[clientsNumber];
		if(clientsNumber == 1)
			splitBroadcastImage[0] = Mat(picture,Rect_<int>(0,0,cols,rows));
		else
		{
			for(int i = 0;i<clientsNumber;i++)
			{
				r = (int)(ROWS*load[i])/100;
				if(i == clientsNumber)
				{
					if((y+r)!=rows)
					{
						r = rows-y;
						cout<<"row: "<<r<<endl;
					}
					splitBroadcastImage[i] = Mat(picture,Rect_<int>(0,y,cols,r));
				}
				else
					splitBroadcastImage[i] = Mat(picture,Rect_<int>(0,y,cols,r+overlap));
				y=y+r;
			}
		}
		double slicing_image_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Splitting image time: T+"<<slicing_image_time*1000<<"ms"<<endl;

		//Processing
		Mat broadcastImage;
		detector->detect(splitBroadcastImage[slice],kp);
		descriptorExtractor->compute(splitBroadcastImage[slice],kp,descriptors);
		double p_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Client processing time: T+"<<p_time*1000<<"ms"<<endl;
		cout<<"Keypoints found: "<<kp.size()<<endl;
		cout<<"SENDING DATA TO THE SERVER"<<endl;
		//opening TCP socket

		sendData(sockfd,p_time*1000,kp.size(),kp,descriptors);
	}
}


